package com.example.locationreport.locationreport;

import android.content.ContentValues;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import com.example.locationreport.locationreport.db.Locations;
import com.example.locationreport.locationreport.db.ReissueLocations;
import com.example.locationreport.locationreport.http.Url;
import com.example.locationreport.locationreport.utils.CommonUtils;
import com.example.locationreport.locationreport.utils.JsonUtils;
import com.example.locationreport.locationreport.utils.LogLocationUtils;
import com.example.locationreport.locationreport.utils.ReissueUtils;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import org.litepal.LitePal;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;

/**
 * author : King
 * date   : 2023/11/1019:01
 * desc   : 实时发送消息
 */
public class RabbitMQClient {

    private static final String TAG = RabbitMQClient.class.getSimpleName();

    private static RabbitMQClient singleRabbitMQClient;

    private final ConnectionFactory factory;
    private Connection connection;

    private List<String> exchangeTypeList = new ArrayList<>(4);

    private Map<String, Channel> channelMap = new HashMap<>();

    private final String FLAG_SEND = "send";

    public static RabbitMQClient getInstance() {
        if (singleRabbitMQClient == null) {
            synchronized (RabbitMQClient.class) {
                if (singleRabbitMQClient == null) {
                    singleRabbitMQClient = new RabbitMQClient();
                }
            }
        }
        return singleRabbitMQClient;
    }

    private RabbitMQClient() {
        //初始化 mq 设置
        factory = new ConnectionFactory();
        //主机地址
        factory.setHost(BuildConfig.MqttUrl);
        //端口
        factory.setPort(BuildConfig.MqttSendProt);
        //用户名
        factory.setUsername(BuildConfig.MqttUserName);
        //密码
        factory.setPassword(BuildConfig.MqttPassword);

        factory.setConnectionTimeout(1000 * 160);   //连接时间设置为10秒
        factory.setAutomaticRecoveryEnabled(true);   //恢复连接，通道
        factory.setTopologyRecoveryEnabled(true);    //恢复通道中 转换器，队列，绑定关系等
        factory.setNetworkRecoveryInterval(1000);    //恢复连接间隔，默认5秒

        exchangeTypeList.add("fanout"); //不用匹配路由，发送给所有绑定转换器的队列
        exchangeTypeList.add("direct"); //匹配路由一致，才发送给绑定转换器队列
        exchangeTypeList.add("topic");  // 通配符* 和 # 匹配路由一致，才发送给绑定转换器队列
        exchangeTypeList.add("headers");

        LogLocationUtils.writeLog(TAG, "RabbitMQ连接:" + BuildConfig.MqttUrl + ":" + BuildConfig.MqttSendProt);
        LogLocationUtils.writeLog(TAG, "账号:" + BuildConfig.MqttUserName + ",密码:" + BuildConfig.MqttPassword);
    }

    /**
     * 自定义转换器名称（指定 fanout 类型转换器，发送到绑定该转换器的所有队列中）
     */
    public void sendExchangeMessage(String message, @NonNull String exchangeName) throws Exception {
        if (!CommonUtils.isConnectIsNormal()) {

            LogLocationUtils.writeLog(TAG, "sendExchangeMessage当前没有网络");

            throw new Exception("sendExchangeMessage当前没有网络");
        }

        if (connection == null || !connection.isOpen()) {
            try {
                connection = factory.newConnection();
            } catch (IOException e) {
                LogLocationUtils.writeLog(TAG, "异常信息factory.newConnection报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();

                throw new Exception(exchangeName + ",异常信息factory.newConnection报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            } catch (TimeoutException e) {
                LogLocationUtils.writeLog(TAG, "异常信息factory.newConnection报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();

                throw new Exception(exchangeName + ",异常信息factory.newConnection报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            } catch (Exception e) {
                LogLocationUtils.writeLog(TAG, "异常信息factory.newConnection报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();

                throw new Exception(exchangeName + ",异常信息factory.newConnection报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            }
        }

        if (!channelMap.containsKey(FLAG_SEND + exchangeName) && channelMap.get(FLAG_SEND + exchangeName) == null) {
            Channel channel;
            try {
                channel = connection.createChannel();
            } catch (IOException e) {
                LogLocationUtils.writeLog(TAG, "异常信息connection.createChannel报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();

                throw new Exception(exchangeName + ",异常信息connection.createChannel报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            } catch (Exception e) {
                LogLocationUtils.writeLog(TAG, "异常信息connection.createChannel报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();

                throw new Exception(exchangeName + ",异常信息connection.createChannel报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            }

            if (!TextUtils.isEmpty(exchangeName)) {
                try {
                    channel.exchangeDeclare(exchangeName, BuiltinExchangeType.FANOUT, true);
                } catch (IOException e) {
                    LogLocationUtils.writeLog(TAG, "异常信息channel.exchangeDeclare报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                    e.printStackTrace();

                    throw new Exception(exchangeName + ",异常信息channel.exchangeDeclare报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                } catch (Exception e) {
                    LogLocationUtils.writeLog(TAG, "异常信息channel.exchangeDeclare报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                    e.printStackTrace();

                    throw new Exception(exchangeName + ",异常信息channel.exchangeDeclare报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                }
            } else {
                LogLocationUtils.writeLog(TAG, "异常信息转换器名字不能为空！");

                throw new Exception(exchangeName + ",异常信息，转换器名字不能为空!");
            }

            channelMap.put(FLAG_SEND + exchangeName, channel);
        }

        try {
            channelMap.get(FLAG_SEND + exchangeName).basicPublish(exchangeName, "", null, message.getBytes());
            try { //记录未上传的定位数据
                Locations LocationsData = JsonUtils.parseJsonToBean(message, Locations.class);   //实时数据
                if (LocationsData != null) {
                    String isCache = LocationsData.getIsCache();//是不是离线缓存数据 0: 在线，1 : 离线
                    if (!"1".equals(isCache)) {
                        //查库
                        List<ReissueLocations> reissueLocationsList = LitePal.order("TE desc").find(ReissueLocations.class);
                        if (reissueLocationsList != null && reissueLocationsList.size() > 0) {
                            ReissueLocations last = LitePal.order("TE desc").findFirst(ReissueLocations.class);
                            if (last != null && LocationsData != null) {
                                String te1 = LocationsData.getTE();//实时数据时间
                                String te2 = last.getTE();//数据库最后数据的时间
                                if (te1 != null && te2 != null) {
                                    long l1 = ReissueUtils.dateToTimestamp(te1, "yyyy-MM-dd HH:mm:ss");//实时时间转时间戳
                                    long l2 = ReissueUtils.dateToTimestamp(te2, "yyyy-MM-dd HH:mm:ss");//数据库时间转时间戳
                                    if (l1 >= l2) {
                                        long diff = l1 - l2;//获取两个时间戳相差
                                        long seconds = diff / 1000;// 将差值转换为秒
                                        int a = (int) seconds;
                                        if (seconds > 1) {
                                            LogLocationUtils.writeLog(TAG, "间隔 > 1：" + a);
                                            ContentValues values = new ContentValues();
                                            values.put("TE", last.getTE());
                                            values.put("SEGMENTATION_TIME", a);
                                            values.put("isCache", "1");
                                            LitePal.update(ReissueLocations.class, values, last.getId());

                                            ReissueLocations reissueLocations = new ReissueLocations();
                                            reissueLocations.setTE(LocationsData.getTE());
                                            reissueLocations.setSEGMENTATION_TIME(0);
                                            reissueLocations.setIsCache("1");
                                            reissueLocations.save();

                                        } else {
                                            LogLocationUtils.writeLog(TAG, "间隔 < 1：");
                                            ContentValues values = new ContentValues();
                                            values.put("TE", LocationsData.getTE());
                                            values.put("SEGMENTATION_TIME", 0);
                                            values.put("isCache", "1");
                                            LitePal.update(ReissueLocations.class, values, last.getId());
                                        }
                                    }
                                }
                            }
                        } else {
                            if (LocationsData != null && LocationsData.getTE() != null) {
                                LogLocationUtils.writeLog(TAG, "补发数据库为空需新增");
                                ReissueLocations reissueLocations = new ReissueLocations();
                                reissueLocations.setTE(LocationsData.getTE());
                                reissueLocations.setSEGMENTATION_TIME(0);
                                reissueLocations.setIsCache("1");
                                reissueLocations.save();
                            }
                        }
                    }
                }
            } catch (Exception e) {
                LogLocationUtils.writeLog(TAG, "补发数据库报错：" + e.getCause() + "---" + e.getMessage() + "---" + message);
                e.printStackTrace();
            }


        } catch (IOException e) {
            LogLocationUtils.writeLog(TAG, "异常信息basicPublish报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            e.printStackTrace();
            throw new Exception(exchangeName + ",异常信息basicPublish报IOException：" + e.getCause() + "---" + e.getMessage() + "---" + message);
        } catch (Exception e) {
            LogLocationUtils.writeLog(TAG, "异常信息basicPublish报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
            e.printStackTrace();

            throw new Exception(exchangeName + ",异常信息basicPublish报Exception：" + e.getCause() + "---" + e.getMessage() + "---" + message);
        }

        LogLocationUtils.writeLog(TAG, "栈名称:" + exchangeName + ",发送消息:" + message);
    }

    /**
     * 关闭所有资源
     */
    public void close() {
        for (Channel next : channelMap.values()) {
            if (next != null && next.isOpen()) {
                try {
                    next.close();
                } catch (IOException | TimeoutException e) {
                    e.printStackTrace();
                }
            }
        }
        channelMap.clear();
        if (connection != null && connection.isOpen()) {
            try {
                connection.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
